/*

  json_object.h
  Shan.Lib

 Copyright (c) 2013, Sung-Han Park
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 1. Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef _Shan_JSON_json_object_h_
#define _Shan_JSON_json_object_h_

#include <map>
#include <memory>
#include <sstream>
#include <algorithm>
#include <type_traits>
#include "json_exception.h"
#include "json_value.h"
#include "json_array.h"
#include "json_string.h"
#include "json_number.h"
#include "json_true.h"
#include "json_false.h"
#include "json_null.h"

namespace shan {
	namespace json {
		class array;

		class object: public json::value, public std::map<string, value::value_ptr> {
		public:
			typedef std::map<key_type, mapped_type> base_type;

			// constructors
			object() {
			}

			object(const object& obj) :
			base_type(obj) {
			}

			object(object&& obj) :
			base_type(std::move(obj)) {
			}

			object(const base_type& obj) :
			base_type(obj) {
			}

			object(base_type&& obj) :
			base_type(std::move(obj)) {
			}

			// destructor
			virtual ~object() {}

			// assignment operators
			object& operator=(const object& obj) {
				base_type::operator=(obj);
				return *this;
			}

			object& operator=(object&& obj) {
				base_type::operator=(std::move(obj));
				return *this;
			}

			object& operator=(const base_type& obj) {
				base_type::operator=(obj);
				return *this;
			}

			object& operator=(base_type&& obj) {
				base_type::operator=(std::move(obj));
				return *this;
			}

			// put functions
			// object
			template<class K = std::string>
			void put(K&& key, const object& val) {
				mapped_type second = std::make_shared<object>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, object&& val) {
				mapped_type second = std::make_shared<object>(std::move(val));
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// array
			template<class K = std::string>
			void put(K&& key, const array& val) {
				mapped_type second = std::make_shared<array>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, array&& val) {
				mapped_type second = std::make_shared<array>(std::move(val));
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// string
			template<class K = std::string>
			void put(K&& key, const string& val) {
				mapped_type second = std::make_shared<string>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, string&& val) {
				mapped_type second = std::make_shared<string>(std::move(val));
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// number
			template<class K = std::string>
			void put(K&& key, const number val) {
				mapped_type second = std::make_shared<number>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// bool
			template<class K = std::string>
			void put(K&& key, const true_value val) {
				mapped_type second = std::make_shared<true_value>();
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, const false_value val) {
				mapped_type second = std::make_shared<false_value>();
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// null
			template<class K = std::string>
			void put(K&& key, const null_value val) {
				mapped_type second = std::make_shared<null_value>();
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// std::string
			template<class K = std::string>
			void put(K&& key, const std::string& val) {
				mapped_type second = std::make_shared<string>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, std::string&& val) {
				mapped_type second = std::make_shared<string>(std::move(val));
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// primitive types...
			template<class K = std::string>
			void put(K&& key, const char* val) {
				mapped_type second = std::make_shared<string>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, char val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, unsigned char val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, short val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, unsigned short val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, int val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, unsigned int val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, long val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, unsigned long val) {
				mapped_type second = std::make_shared<number>((long long)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, long long val) {
				mapped_type second = std::make_shared<number>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, unsigned long long val) {
				mapped_type second = std::make_shared<number>((long long)val); // will lose some data.
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, float val) {
				mapped_type second = std::make_shared<number>((double)val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, double val) {
				mapped_type second = std::make_shared<number>(val);
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, bool val) {
				mapped_type second = val ? std::dynamic_pointer_cast<value>(std::make_shared<true_value>()) : std::dynamic_pointer_cast<value>(std::make_shared<false_value>());
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			template<class K = std::string>
			void put(K&& key, std::nullptr_t val) {
				mapped_type second = std::make_shared<null_value>();
				std::pair<iterator, bool> res = insert(std::make_pair(std::forward<K>(key), second));
				if (!res.second)
					res.first->second = second;
			}

			// get functions
			std::shared_ptr<json::object> get_object(const key_type& key, std::shared_ptr<json::object> defValue = std::shared_ptr<json::object>());
			std::shared_ptr<json::array> get_array(const key_type& key, std::shared_ptr<json::array> defValue = std::shared_ptr<json::array>());

			std::string get_string(const key_type& key, std::string defValue = std::string()) {
				try {
					return base_type::at(key)->to_string();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			int get_int(const key_type& key, int defValue = 0) {
				try {
					return base_type::at(key)->to_int();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			long get_long(const key_type& key, long defValue = 0) {
				try {
					return base_type::at(key)->to_long();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			long long get_long_long(const key_type& key, long long defValue = 0) {
				try {
					return base_type::at(key)->to_long_long();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			float get_float(const key_type& key, float defValue = 0) {
				try {
					return base_type::at(key)->to_float();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			double get_double(const key_type& key, double defValue = 0) {
				try {
					return base_type::at(key)->to_double();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			bool get_bool(const key_type& key, bool defValue = false) {
				try {
					return base_type::at(key)->to_bool();
				} catch (const std::out_of_range&) {
					return defValue;
				} catch (const std::exception& e) {
					throw json::exception(e.what());
				}
			}

			virtual int to_int() {
				throw json::exception("json::object is not compatible with int type.");
			}

			virtual long to_long() {
				throw json::exception("json::object is not compatible with long type.");
			}

			virtual long long to_long_long() {
				throw json::exception("json::object is not compatible with long long type.");
			}

			virtual float to_float() {
				throw json::exception("json::object is not compatible with float type.");
			}

			virtual double to_double() {
				throw json::exception("json::object is not compatible with double type.");
			}

			virtual bool to_bool() {
				throw json::exception("json::object is not compatible with bool type.");
			}
			
			virtual bool is_null() {
				return false; // even empty object is not null
			}
			
			virtual std::string to_json() const;
			virtual std::string to_string() const {
				return to_json();
			}
			
		protected:
			virtual void internal_parse(std::istream& is);
		};
	}
}

#endif // _Shan_JSON_json_object_h_
